package com.youlu.campus.activity.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.youlu.campus.activity.entity.ActivityLotteryConfig;
import com.youlu.campus.activity.entity.ActivityLotteryConfigPrizeVo;
import com.youlu.campus.activity.entity.ActivityLotteryGiftNum;
import com.youlu.campus.activity.entity.ActivityLotteryVo;
import com.youlu.campus.activity.entity.UserLotteryInfo;
import com.youlu.campus.activity.entity.UserLotteryInfoVo;
import com.youlu.campus.activity.entity.UserLotteryRecord;
import com.youlu.campus.activity.enums.ActivityLotteryPrizeType;
import com.youlu.campus.activity.repositories.UserLotteryInfoRepository;
import com.youlu.campus.activity.service.ActivityInfoService;
import com.youlu.campus.activity.service.ActivityLotteryConfigPrizeService;
import com.youlu.campus.activity.service.ActivityLotteryConfigService;
import com.youlu.campus.activity.service.UserActivityTaskRankingService;
import com.youlu.campus.activity.service.UserLotteryInfoService;
import com.youlu.campus.activity.service.UserLotteryRecordService;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.common.utils.RedisKeyUtil;
import com.youlu.campus.entity.ActivityInfo;
import com.youlu.campus.entity.UserPO;
import com.youlu.campus.entity.enums.TaskBizType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description: UserLotteryInfoServiceImpl
 * @Date 2025/7/31
 * @module UserLotteryInfoServiceImpl
 * @author: su
 */
@Slf4j
@Service
public class UserLotteryInfoServiceImpl implements UserLotteryInfoService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private UserLotteryInfoRepository userLotteryInfoRepository;

    @Autowired
    private ActivityInfoService activityInfoService;

    @Autowired
    private ActivityLotteryConfigService activityLotteryConfigService;

    @Autowired
    private UserActivityTaskRankingService userActivityTaskRankingService;

    @Autowired
    private UserLotteryRecordService userLotteryRecordService;

    @Autowired
    private ActivityLotteryConfigPrizeService activityLotteryConfigPrizeService;

    @Override
    public UserLotteryInfoVo getUserLotteryInfo(String activityId, UserPO curUser) {
        UserLotteryInfo userLotteryInfo = userLotteryInfoRepository.findByActivityIdAndUserId(activityId, curUser.getId());
        //查询用户贡献值
        int quantity = getUserQuantity(activityId, curUser);
        if (userLotteryInfo == null) {
            userLotteryInfo = initUserLotteryInfo(activityId, curUser.getId(), quantity);
        }
        UserLotteryInfoVo userLotteryInfoVo = new UserLotteryInfoVo();
        BeanUtils.copyProperties(userLotteryInfo, userLotteryInfoVo);
        userLotteryInfoVo.setUserNickeName(curUser.getNickName());
        userLotteryInfoVo.setUserAvatarUrl(curUser.getAvatarUrl());
        userLotteryInfoVo.setUserPoints(quantity);
        return userLotteryInfoVo;
    }

    /**
     * 获取用户贡献值
     *
     * @param activityId
     * @param curUser
     * @return
     */
    private int getUserQuantity(String activityId, UserPO curUser) {
        Double quantity = userActivityTaskRankingService.getQuantity(curUser.getId(), activityId);
        return quantity.intValue();
    }

    /**
     * 初始化 用户抽奖信息
     *
     * @param activityId
     * @param userId
     * @param quantity
     * @return
     */
    private UserLotteryInfo initUserLotteryInfo(String activityId, String userId, int quantity) {
        //TODO 查询当前用户的
        ActivityLotteryConfig activityLotteryConfig = activityLotteryConfigService.findByActivityId(activityId);
        //赠送次数
        List<ActivityLotteryGiftNum> giftNums = activityLotteryConfig.getGiftNums();
        int availableDraws = 0;
        if (CollectionUtils.isNotEmpty(giftNums)) {
            //获取符合条件的  赠送次数
            availableDraws = giftNums.stream().filter(gift -> gift.getMaxPoints() > quantity && gift.getMinPoints() <= quantity).mapToInt(ActivityLotteryGiftNum::getNum).sum();
        }
        //添加用户抽奖信息
        UserLotteryInfo userLotteryInfo = new UserLotteryInfo();
        userLotteryInfo.setUserId(userId);
        userLotteryInfo.setActivityId(activityId);
        userLotteryInfo.setAvailableDraws(availableDraws);
        userLotteryInfo.setDrawnCount(0);
        return userLotteryInfoRepository.save(userLotteryInfo);
    }


    @Override
    public UserLotteryInfo save(UserLotteryInfo userLotteryInfo) {

        return userLotteryInfoRepository.save(userLotteryInfo);
    }

    @Override
    public ActivityLotteryConfigPrizeVo draw(String activityId, UserPO curUser) {
        log.info("draw  抽奖, userId:{}, activityId:{}", curUser.getId(), activityId);
        String lockKey = RedisKeyUtil.getActivityLotteryDrawLock(curUser.getId(), activityId);
        Boolean existKey = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS);
        log.info("draw  抽奖, 获取锁 existKey:{}", existKey);
        try {
            if (Objects.nonNull(existKey) && !existKey) {
                log.error("draw 抽奖重复请求, :{},:{}", curUser.getId(), activityId);
                throw new BusinessException("您的手速好快哦,请稍后操作哦~");
            }
            // 1. 校验活动是否有效（状态、时间范围）
            ActivityInfo activityInfo = activityInfoService.findById(activityId);
            if (Objects.isNull(activityInfo)) {
                throw new BusinessException("活动不存在");
            }
            Date date = new Date();
            if (date.before(activityInfo.getStartTime())) {
                log.error("draw activity is not start id:{}, startTime:{}", activityId, activityInfo.getStartTime());
                throw new BusinessException("活动未开始!");
            }
            if (date.after(activityInfo.getEndTime())) {
                log.error("draw activity is end id:{}, endTime:{}", activityId, activityInfo.getEndTime());
                throw new BusinessException("活动已经结束!");
            }
            //抽奖 配置信息
            ActivityLotteryVo activityLotteryVo = activityLotteryConfigService.infoByEnable(activityId);
            if (Objects.isNull(activityLotteryVo)) {
                throw new BusinessException("抽奖活动未开启");
            }
            // 今日抽奖次数key
            String userTodayCountKey = RedisKeyUtil.getActivityLotteryUserTodayCountKey(curUser.getId(), activityId);
            if (activityLotteryVo.getDailyDrawLimit() > 0) {
                String userTodayCount = redisTemplate.opsForValue().get(userTodayCountKey);
                if (StringUtils.isNotBlank(userTodayCount) && Integer.parseInt(userTodayCount) > activityLotteryVo.getDailyDrawLimit()) {
                    throw new BusinessException("您今日已无抽奖次数,请明天再来吧!");
                }
            }
            List<ActivityLotteryConfigPrizeVo> configPrizes = activityLotteryVo.getPrizes();
            //过滤 未中奖 与 未抽完的 奖品
            configPrizes = configPrizes.stream().filter(p -> ActivityLotteryPrizeType.NO.getType().equals(p.getPrizeType()) || p.getRemainingCount() > 0).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(configPrizes)) {
                throw new BusinessException("奖品不足!");
            }
            UserLotteryInfo userLotteryInfo = userLotteryInfoRepository.findByActivityIdAndUserId(activityId, curUser.getId());
            if (userLotteryInfo.getAvailableDraws() > 0) {
                //抽奖
                userLotteryInfo.setAvailableDraws(userLotteryInfo.getAvailableDraws() - 1);
            } else {
                //查询用户贡献值
                int quantity = getUserQuantity(activityId, curUser);
                if (quantity < activityLotteryVo.getPoints()) {
                    throw new BusinessException("您的" + activityLotteryVo.getPointsAlias() + "不足,可以邀请好友获取哦~");
                } else {
                    double points = -activityLotteryVo.getPoints();
                    //减用户贡献值
                    userActivityTaskRankingService.addQuantityByUserIdAndActivityId(curUser.getId(), activityId, points, TaskBizType.LOTTERY);
                }
            }
            Long userTodayCount = redisTemplate.opsForValue().increment(userTodayCountKey, 1);
            userLotteryInfo.setTodayCount(userTodayCount.intValue());
            userLotteryInfo.setDrawnCount(userLotteryInfo.getDrawnCount() + 1);
            userLotteryInfo.setLastDrawTime(LocalDateTime.now());
            userLotteryInfoRepository.save(userLotteryInfo);
            //抽奖
            ActivityLotteryConfigPrizeVo activityLotteryPrize = this.doDraw(configPrizes);
            activityLotteryPrize.setActivityName(activityInfo.getName());
            activityLotteryPrize.setCustomerService(activityLotteryVo.getCustomerService());
            //添加中奖记录
            UserLotteryRecord userLotteryRecord = addUserLotteryRecord(curUser, activityInfo, activityLotteryPrize);

            //修改奖品 剩余数量
            String prizeDrawdCountKey = RedisKeyUtil.getActivtyLotteryPrizeWinCount(activityLotteryPrize.getConfigPrizeId());
            Long prizeDrawdCount = redisTemplate.opsForValue().increment(prizeDrawdCountKey, activityLotteryPrize.getOneCount());
            long remainingCount = activityLotteryPrize.getTotalCount() - prizeDrawdCount;
            activityLotteryConfigPrizeService.updateRemainingCountById(activityLotteryPrize.getConfigPrizeId(), remainingCount);
            // 贡献值直接领取
            if (activityLotteryPrize.getPrizeType() == 3) {
                claimPrizesPoints(userLotteryRecord);
            }
            log.info("draw  activityLotteryPrize :{}", JSONObject.toJSONString(activityLotteryPrize));
            return activityLotteryPrize;
        } catch (BusinessException e) {
            throw e;
        }
    }

    /**
     * 添加用户 中奖记录
     *
     * @param curUser
     * @param activityInfo
     * @param activityLotteryPrize
     */
    private UserLotteryRecord addUserLotteryRecord(UserPO curUser, ActivityInfo activityInfo, ActivityLotteryConfigPrizeVo activityLotteryPrize) {
        UserLotteryRecord record = new UserLotteryRecord();
        record.setUserId(curUser.getId());
        record.setMobile(curUser.getMobile());
        record.setUserNickeName(StringUtils.isNotBlank(curUser.getNickName()) ? curUser.getNickName() : curUser.getRankNickName());
        record.setActivityId(activityInfo.getId());
        record.setActivityName(activityInfo.getName());
        record.setLotteryId(activityLotteryPrize.getLotteryId());
        record.setPrizeId(activityLotteryPrize.getId());
        record.setPrizeName(activityLotteryPrize.getPrizeName());
        record.setPrizeImg(activityLotteryPrize.getPrizeImg());
        record.setPrizeType(activityLotteryPrize.getPrizeType());
        record.setOneCount(activityLotteryPrize.getOneCount());
        record.setDrawTime(LocalDateTime.now());
        record.setStatus(activityLotteryPrize.getPrizeType() == 0 ? 1 : 0);
        record.setCustomerService(activityLotteryPrize.getCustomerService());
        return userLotteryRecordService.addRecord(record);
    }


    /**
     * 实际抽奖算法（根据奖品概率随机生成结果）
     */
    public ActivityLotteryConfigPrizeVo doDraw(List<ActivityLotteryConfigPrizeVo> prizes) {
        //总权重
        int totalWeight = prizes.stream().mapToInt(ActivityLotteryConfigPrizeVo::getProbability).sum();
        //随机数
        int choice = ThreadLocalRandom.current().nextInt(totalWeight); // 使用ThreadLocalRandom获取随机数
        int j = 0;

        for (int i = 0; i < prizes.size(); i++) {
            ActivityLotteryConfigPrizeVo activityLotteryPrize = prizes.get(i);
            int probability = activityLotteryPrize.getProbability();

            if (choice < probability) { // 直接比较是否小于当前权重值来决定是否选中当前选项。
                return activityLotteryPrize;
            } else { // 从剩余权重中减去当前权重，并继续检查下一个选项。
                choice -= probability;
            }
            j = i; // 更新最后一次选中的选项索引。
        }
        return prizes.get(j);// 所有权重都检查完毕后，选择最后一个。
    }

    /**
     * 领取抽奖 贡献值
     *
     * @param record
     */
    private void claimPrizesPoints(UserLotteryRecord record) {
        if (record.getStatus() == 1) {
            log.error("claimPrizes 奖品已领取 recordId:{}", record.getId());
            return;
        }
        if (record.getPrizeType() != 3) {
            log.error("claimPrizes 非贡献值 recordId:{},prizeType:{}", record.getId(), record.getPrizeType());
            return;
        }
        //添加贡献值
        userActivityTaskRankingService.addQuantityByUserIdAndActivityId(record.getUserId(), record.getActivityId(), Double.valueOf(record.getOneCount()), TaskBizType.LOTTERY);

        //更新记录
        record.setStatus(1);
        record.setReceiveTime(LocalDateTime.now());
        userLotteryRecordService.save(record);

    }
}
